package repository

import (
	"context"
	"errors"
	"github.com/ecodeclub/ekit/slice"
	"jk-time/webook/pkg/logger"

	"jk-time/webook/tag/cache"
	"jk-time/webook/tag/dao"
	"jk-time/webook/tag/domain"
	"time"
)

var ErrRecordnotFound = dao.ErrRecordnotFound

type TagRepository interface {
	CreateTag(ctx context.Context, tag domain.Tag) (int64, error)
	BindTagToBiz(ctx context.Context, uid int64, biz string, bizId int64, tags []int64) error
	GetTags(ctx context.Context, uid int64) ([]domain.Tag, error)
	GetTagsById(ctx context.Context, ids []int64) ([]domain.Tag, error)
	GetBizTags(ctx context.Context, uid int64, biz string, bizId int64) ([]domain.Tag, error)
}
type tagRepository struct {
	dao   dao.TagtDao
	cache cache.Cache
	l     logger.Logger
}

func NewTagRepository(dao dao.TagtDao, cache cache.Cache, l logger.Logger) TagRepository {
	repo := &tagRepository{dao: dao, cache: cache, l: l}
	go func() {
		//缓存预加载
		ctx, cancel := context.WithCancel(context.Background())
		defer cancel()
		err := repo.PreloadUserTags(ctx)
		if err != nil {
			repo.l.Error("预加载缓存:", logger.Error(err))
		}
	}()
	return repo
}

// PreloadUserTags 在 toB 的场景下，你可以提前预加载缓存
func (t *tagRepository) PreloadUserTags(ctx context.Context) error {
	offset := 0
	limit := 100
	for {
		dbCtx, cancel := context.WithTimeout(ctx, time.Second)
		tags, err := t.dao.GetTags(dbCtx, offset, limit)
		cancel()
		if err != nil {
			if errors.Is(err, ErrRecordnotFound) {
				return nil
			}
			return err
		}

		for _, tag := range tags {
			rCtx, cancel := context.WithTimeout(ctx, time.Second)
			err := t.cache.SetTags(rCtx, tag.Uid, t.toDomain(tag))
			cancel()
			if err != nil {
				continue
			}
		}
		if len(tags) < limit {
			return nil
		}
		offset = offset + limit
	}
}
func (t *tagRepository) CreateTag(ctx context.Context, tag domain.Tag) (int64, error) {
	id, err := t.dao.CreateTag(ctx, t.toEntity(tag))
	if err != nil {
		return 0, err
	}
	err = t.cache.SetTags(ctx, tag.Uid, tag)
	if err != nil {
		// 记录日志就可以
		t.l.Error("缓存标签到数据库失败:", logger.Int64("uid", tag.Uid), logger.Error(err))
	}
	return id, nil
}
func (t *tagRepository) GetTags(ctx context.Context, uid int64) ([]domain.Tag, error) {
	tags, err := t.cache.GetTags(ctx, uid)
	if err == nil {
		return tags, err
	}

	res, err := t.dao.GetTagsByUid(ctx, uid)
	if err == nil {
		return tags, err
	}
	tags = slice.Map(res, func(idx int, src dao.Tag) domain.Tag {
		return t.toDomain(src)
	})
	go func() {
		err = t.cache.SetTags(ctx, uid, tags...)
		if err != nil {
			t.l.Error("缓存标签到数据库失败:", logger.Int64("uid", uid), logger.Error(err))
		}
	}()
	return tags, nil
}
func (t *tagRepository) GetTagsById(ctx context.Context, ids []int64) ([]domain.Tag, error) {
	tags, err := t.dao.GetTagsById(ctx, ids)
	if err != nil {
		return nil, err
	}
	return slice.Map(tags, func(idx int, src dao.Tag) domain.Tag {
		return t.toDomain(src)
	}), nil
}
func (t *tagRepository) GetBizTags(ctx context.Context, uid int64, biz string,
	bizId int64) ([]domain.Tag, error) {
	// 这里要不要缓存？
	tags, err := t.dao.GetTagsByBiz(ctx, uid, biz, bizId)
	if err != nil {
		return nil, err
	}
	return slice.Map(tags, func(idx int, src dao.Tag) domain.Tag {
		return t.toDomain(src)
	}), nil
}
func (t *tagRepository) BindTagToBiz(ctx context.Context, uid int64, biz string, bizId int64,
	tags []int64) error {
	return t.dao.CreateTagBiz(ctx, slice.Map(tags, func(idx int, src int64) dao.TagBiz {
		return dao.TagBiz{
			Tid:   src,
			BizId: bizId,
			Biz:   biz,
			Uid:   uid,
		}
	}))
}
func (repo *tagRepository) toDomain(tag dao.Tag) domain.Tag {
	return domain.Tag{
		Id:   tag.Id,
		Name: tag.Name,
		Uid:  tag.Uid,
	}
}

func (repo *tagRepository) toEntity(tag domain.Tag) dao.Tag {
	return dao.Tag{
		Id:   tag.Id,
		Name: tag.Name,
		Uid:  tag.Uid,
	}
}
